<?php

namespace app\api\controller\pay;

use think\Db;
use app\api\controller\Common;

require "../extend/wxpay/lib/WxPay.Api.php";
require "../extend/wxpay/lib/WeixinPay.php";
class Applet extends Common
{

    private $appid;
    private $mch_id;
    private $api_key;
    private $notify_url;

    public function initialize()
    {
        parent::initialize();
        $config = config('wxpay');
        $this->appid = $config['appid'];
        $this->mch_id = $config['mch_id'];
        $this->api_key = $config['api_key'];
        $this->notify_url = $config['pay_notify_url'];
    }

    /**
     * 微信支付预下单
     *
     * @return void
     */
    public function wxPay()
    {

        $order_id = input('post.order_id');
        if (empty($order_id)) {
            return $this->showError('参数错误','401');
        }
        $this->checkUserLogin();
        $openid = $this->user['openid'];
        $user_id = $this->user['id'];

        $order = Db::name('order_info')->where(['id' => $order_id])->find();
        if (empty($order)) {
            return $this->showError('订单不存在', '403');
        }

        $notify_url = $this->notify_url;
        $out_trade_no = $order['order_no'] . rand(1000, 9999); // 商户订单号
        $total_fee = $order['order_price'] * 100; //需要支付的金额 单位分
        // $total_fee = 1; //TODO 支付 1 分
        $body = 'VVME-下单';
        $attach = json_encode(['order_id' => $order_id]);

        //下单
        $weixinpay = new \WeixinPay($this->appid, $openid, $this->mch_id, $this->api_key, $out_trade_no, $body, $total_fee, $notify_url, $attach);
        $return = $weixinpay->pay();

        //记录日志
        Db::name('order_pay_log')->insert([
            'user_id' => $user_id,
            'openid' => $openid,
            'order_id' => $order_id,
            'order_type' => $order['order_type'],
            'out_trade_no' => $out_trade_no,
            'total_fee' => $total_fee,
            'body' => $body,
            'attach' => $attach,
            'operate' => 'unifiedorder',
            'create_time' => toDate(time()),
        ]);

        return $this->showSuccess('支付预下单成功', $return);
    }

    /**
     * 余额支付
     *
     * @return void
     */
    public function balancePay()
    {

        $order_id = input('post.order_id');
        if (empty($order_id)) {
            return $this->showError('参数错误', '401');
        }
        $this->checkUserLogin();
        $user_id = $this->user['id'];
        $user_account = db('users')->where(['id' => $user_id])->value('account');

        $order = Db::name('order_info')->where(['id' => $order_id])->find();
        if (empty($order)) {
            return $this->showError('订单不存在', '403');
        }
        if ($order['pay_status'] == 1) {
            return $this->showError('订单已支付，请不要重复支付', '406');
        }

        if ($order['order_price'] > $user_account) {
            return $this->showError('余额不足', '405');
        }
        Db::name('users')->where(['id' => $order['user_id']])->setDec('account', $order['order_price']);
        $order = Db::name('order_info')->where(['id' => $order_id])->update([
            'order_status' => 2,
            'pay_status' => 1,
            'pay_method' => 2,
            'pay_time' => date('Y-m-d H:i:s'),
        ]);

        return $this->showSuccess('余额支付成功');
    }

    public function notify()
    {

        $postXml = file_get_contents("php://input"); //接收微信参数

        if (empty($postXml)) {
            trace('parse xml error', 'wx_notify');
            return json(['code' => 400, 'msg' => "失败"]);
        }

        //将 xml 格式转换成数组
        $data = $this->xmlToArray($postXml);
        trace(json_encode($data), 'wx_notify');
        $data_sign = $data['sign'];

        //验证签名
        $sign = $this->makeSign($data);
        if ($sign != $data_sign) {
            echo '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';
            trace('签名失败: 根据参数签名 ' . $sign . "，传入签名 " . $data_sign, 'wx_notify');
            return false;
        }

        if (($data['return_code'] == 'SUCCESS') && ($data['result_code'] == 'SUCCESS')) {

            $out_trade_no = $data['out_trade_no'];
            $order = Db::name('order_pay_log')->where(['out_trade_no' => $out_trade_no])->find();

            if (empty($order)) {
                trace('无效订单：out_trade_no=' . $out_trade_no, 'wx_notify');
                return false;
            } elseif ($order['status'] == 1) {
                echo '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';   //不写一直回调
                exit;
            } else {
                $notify_data['transaction_id'] = $data['transaction_id'];
                $notify_data['status'] = 1;
                $notify_data['pay_time_end'] = $data['time_end'];
                Db::name('order_pay_log')->where(['id' => $order['id']])->update($notify_data);

                $order = Db::name('order_info')->where(['id' => $order['order_id']])->update([
                    'pay_status' => 1,
                    'pay_time' => $data['time_end'],
                    'order_status' => 2,
                ]);

                echo '<xml><return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg></xml>';   //不写一直回调
                exit;
            }
        } else {
            return false;
        }

        return true;
    }


    /** 
     * 生成签名 
     * @return string 
     */
    private function makeSign($data)
    {

        unset($data['sign']);
        // 去空 
        $data = array_filter($data);

        //按字典序排序参数 
        ksort($data);
        $string_a = http_build_query($data);
        $string_a = urldecode($string_a);

        //在string后加入KEY 
        $string_sign_temp = $string_a . "&key=" . $this->api_key;

        //MD5加密 
        $sign = md5($string_sign_temp);

        // 签名步骤四：所有字符转为大写 
        $result = strtoupper($sign);

        return $result;
    }

    public function xmlToArray($xml)
    {
        //禁止引用外部 xml 实体
        libxml_disable_entity_loader(true);
        $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
        $val = json_decode(json_encode($xmlstring), true);
        return $val;
    }

    /**
     * decrypt data
     * @param string $encryptData
     * @param string $md5LowerKey
     * @return string | FALSE
     */
    public function decryptData(string $encryptData, string $Key = '')
    {
        //1. base64_decode
        // openssl_decrypt only accept base64 input param
        //2. md5 original key
        $md5LowerKey = strtolower(md5($Key));
        //3. decrypt AES ECB
        $decrypted = openssl_decrypt($encryptData, "AES-256-ECB", $md5LowerKey);
        return $decrypted;
    }
}
